package ifmo.compiler.task21.parser;

import ifmo.compiler.task21.parser.symbols.EndSymbol;
import ifmo.compiler.task21.parser.symbols.Nonterminal;
import ifmo.compiler.task21.parser.symbols.OperationSymbol;
import ifmo.compiler.task21.parser.symbols.Symbol;
import ifmo.compiler.task21.parser.symbols.SymbolType;
import ifmo.compiler.task21.parser.symbols.Terminal;

import java.util.*;

public class PredictiveAnalisator {
	private ArrayList<Symbol> _stack = new ArrayList<Symbol>();

	public PredictiveAnalisator() {
	}

	public ArrayList<String> analys(Symbol[] w, PredictiveAnalysTable table,
			Nonterminal startSymbol) throws Exception {
		ArrayList<Production> productions = new ArrayList<Production>();
		ArrayList<String> output = new ArrayList<String>();
		Symbol X = null;
		int ip = 0;

		_stack.add(0, new EndSymbol());
		_stack.add(0, startSymbol);

		do {
			Symbol a = w[ip];
			X = _stack.get(0);

			if (X.getType() == SymbolType.Operation) {
				OperationSymbol op = (OperationSymbol)X;
				_stack.remove(0);
				output.add(op.getLexval() + " ");
			} else if (X.getType() == SymbolType.Terminal
					|| X.getType() == SymbolType.EndSymbol) {
				if (X.equals(a)) {
					_stack.remove(0);
					ip++;
				} else {
					throw new Exception("Error!");
				}
			} else {
				Production prod = table.getProduction((Nonterminal) X, a);

				if (prod != null) {
					_stack.remove(0);
					Symbol[] tail = prod.getTail();

					if (prod.hasRule()) {
						for (Symbol x : tail) {
							if (x.getType() == SymbolType.Operation){
								OperationSymbol op = (OperationSymbol)x;
								Terminal t = (Terminal)a;
								
								op.setLexval(t.getLexval());
								break;
							}
						}
					}
					for (int i = tail.length - 1; i >= 0; i--) {
						if (tail[i].getType() != SymbolType.Epsilon) {
							_stack.add(0, tail[i]);
						}
					}
					productions.add(prod);
				} else {
					throw new Exception("Error!");
				}
			}
		} while (!_stack.get(0).equals(new EndSymbol()));

		return output;
	}
}
